#include "RawTiffOverlay.h"

using namespace std;



namespace {

        static void convert32BitOrder(void *buffer, int width)
        {
            uint32 *target = reinterpret_cast<uint32 *>(buffer);
            for (int32 x=0; x<width; ++x) {
                uint32 p = target[x];
                // convert between ARGB and ABGR
                target[x] = (p & 0xff000000)
                            | ((p & 0x00ff0000) >> 16)
                            | (p & 0x0000ff00)
                            | ((p & 0x000000ff) << 16);
            }
        }

}


TIFFOverlay::TIFFOverlay( const QString filename  )
        : RawOverlay(0.01,filename),d_pCache(NULL),
        d_samplesPerPixel(1)
{
    tif = (TIFF*)0;
}




TIFFOverlay::~TIFFOverlay()
{
    // Close the tif if it's valid
    if ( tif ) {
        TIFFClose(tif);
    }
    // Free the property sheet
    //////delete d_pPropertySheet;

    // Free the data cache
    delete d_pCache;
}

/* ########################################### tiff main load it ###################################################################*/

bool TIFFOverlay::load()
{
    chrono.start();

    num_colors = -1;
    uint32 resUnit = RESUNIT_NONE;

    // Open the TIFF file
    tif = TIFFOpen( qPrintable(getFilename()) , "r" );

    // Abort the load if the TIFF wasn't successfully opened
    if ( !tif )
    {
        AfxMessageBox( "Unable to open TIFF file: " + getFilename() );
        return false;
    }

    // Get photometric interpretation
    if ( !TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &d_photometricInterpretation ))
    {
        AfxMessageBox( "Invalid TIFF: " + getFilename() );
        return false;
    }

    // Get image width
    if ( !TIFFGetField( tif, TIFFTAG_IMAGEWIDTH, &d_imageWidth ) )
    {
        AfxMessageBox( "Invalid TIFF: " + getFilename() );
        return false;
    }

    // Get image length
    if ( !TIFFGetField( tif, TIFFTAG_IMAGELENGTH, &d_imageLength ) )
    {
        AfxMessageBox( "Invalid TIFF: " + getFilename() );
        return false;
    }

    float resX = 72;
    float resY = 72;

    TIFFGetField(tif, TIFFTAG_RESOLUTIONUNIT, &resUnit);
    TIFFGetField(tif, TIFFTAG_XRESOLUTION, &resX);
    TIFFGetField(tif, TIFFTAG_YRESOLUTION, &resY);

    if (resUnit == RESUNIT_CENTIMETER ) {
        resX = resX * 2.54;
        resY = resY * 2.54;
    }

    fDPI = qMax ( resX , resY );

    // Get the number of rows per strip
    if ( !TIFFGetField( tif, TIFFTAG_ROWSPERSTRIP, &d_rowsPerStrip ) )
    {
        AfxMessageBox( "Invalid TIFF: " + getFilename() );
        return false;
    }

    // Get the copyright info
    if ( !TIFFGetField( tif, TIFFTAG_COPYRIGHT, &d_copyright ) )
    {
        d_copyright = NULL;
    }

    // Get the date and time of creation
    if ( !TIFFGetField( tif, TIFFTAG_DATETIME, &d_datetime ) )
    {
        d_datetime = NULL;
    }

    // Get the image description
    if ( !TIFFGetField( tif, TIFFTAG_IMAGEDESCRIPTION, &d_description ) )
    {
        d_description = NULL;
    }

    // Get the number of bits per sample
    if ( !TIFFGetField( tif, TIFFTAG_BITSPERSAMPLE, &d_bitsPerSample ) )
    {
        AfxMessageBox( "Error loading TIFF: " + getFilename() );
        return false;
    }
    
    num_colors = 256;  //// default by init //// 
    
    switch ( d_photometricInterpretation )
    {
    case PHOTOMETRIC_MINISWHITE:
    {
        if (d_bitsPerSample == 1) {
            ctype = BilevelMinIsWhite;
            num_colors = 2;
        } else if ( d_bitsPerSample == 8 ) {
            ctype = Gray8MinIsWhite;
            num_colors = 8;
        }
        break;
    }
    case PHOTOMETRIC_MINISBLACK:
    {
        // Determine the number of colors.  Is this is a special 16-bit TIFF
        // use 256 because we'll scale the values later
          if (d_bitsPerSample == 1) {
            ctype = BilevelMinIsBlack;
            num_colors = 2;
          } else if ( d_bitsPerSample == 8 ) {
            ctype = Gray8MinIsBlack;
            num_colors = 8;
          }
          
        break;
    }
    case PHOTOMETRIC_RGB:
    {
        if ( !TIFFGetField( tif, TIFFTAG_SAMPLESPERPIXEL, &d_samplesPerPixel ) )
        {
            AfxMessageBox( "Error loading TIFF: " + getFilename() );
            return false;
        }
        if ( d_samplesPerPixel < 3 )
        {
            AfxMessageBox( "Error loading TIFF: " + getFilename() );
            return false;
        } else if ( d_samplesPerPixel  == 3 ) {
            ctype = RGB24;
        } else if ( d_samplesPerPixel  == 4 )  {
            ctype = RGB32;
        }
        break;
    }
    case PHOTOMETRIC_PALETTE:
    {
        AfxMessageBox( "Palette TIFFs not supported" );
        return false;
        break;
    }
    case PHOTOMETRIC_MASK:
    {
        AfxMessageBox( "Masked TIFFs not supported" );
        return false;
        break;
    }
    case PHOTOMETRIC_SEPARATED:
    {
        AfxMessageBox( "Separated TIFFs not supported" );
        return false;
        break;
    }
    case PHOTOMETRIC_YCBCR:
    {
        AfxMessageBox( "YCBCR TIFFs not supported" );
        return false;
        break;
    }
    case PHOTOMETRIC_CIELAB:
    {
        AfxMessageBox( "CIELAB TIFFs not supported" );
        return false;
        break;
    }
    default:
    {
        AfxMessageBox( "Invalid Photometric Interpretation" );
        return false;
    }
    }
    Ccolors = colorTable(ctype);
    faktor = 1.;   /* not scale */
    // Create the TIFF data cache
    d_pCache = new TIFFCache( tif, d_rowsPerStrip);
    d_loaded = true;
    imat = new ImageMatrix(QSize(getImageWidth(),getImageHeight()));
    d_boundingRect = QRect(0,0, d_imageWidth, d_imageLength );
    qDebug() << "## image info 1 " << getImageWidth() << "x" << getImageHeight();
    qDebug() << "## image info 2  num_colors ->" << num_colors;
    qDebug() << "## image info 3  photoInterpToString ->" << photoInterpToString();
    qDebug() << "## image info 5  DPI s  ->" << fDPI;
    return d_loaded;
}






void TIFFOverlay::draw( QPainter *p  , const QRect rect , const  QPoint point , QWidget *target )
{
    if (!imat)
        return;

    p->save();
    imat->rescale(rect);
    const uint wi = imat->csize();
    const uint hi = imat->rsize();
    QImage gdi(wi,hi,QImage::Format_Indexed8);
    gdi.setColorTable ( Ccolors );

    //////gdi.setNumColors ( 2 );

    uchar* pBits = gdi.bits();
    int trax = -1;

    int* a_cool = imat->arraycool();
    int* a_row = imat->arrayrow();

    for ( int y = 0; y < hi ; y++ )
    {
        if (y < gdi.height()) {
            int rok;
            if (y%2)
                rok = 0;
            else
                rok = 1;


            for ( int x = 0; x < wi ; x++ ) {
                if (x < gdi.width()) {
                    trax++;
                    pBits[trax] = rok;
                }
            }
        }
    }



    p->drawImage(QPointF(0,0),gdi);

    p->setBrush(Qt::NoBrush);
    p->setPen(QPen(QBrush(QColor(Qt::red)),5.));
    p->drawRect(QRect(30,30,hi,wi));
    p->setPen(QPen(QBrush(QColor(Qt::green)),10.));
    p->drawRect(QRect(10,10,rect.width(),rect.height()));
    qDebug() << "## render   " << imat->irect();
    p->restore();
}









void TIFFOverlay::readMinMaxSampleValues( TIFF* pTIFF )
{
    if (!pTIFF)
        return;
    // Get the maximum sample value
    TIFFGetField( pTIFF, TIFFTAG_MAXSAMPLEVALUE, &d_maxSampleValue );

    // Get the minimum sample value
    TIFFGetField( pTIFF, TIFFTAG_MINSAMPLEVALUE, &d_minSampleValue );
}



QString TIFFOverlay::photoInterpToString() const
{
    // Determine the photometric interpretation name
    switch ( d_photometricInterpretation )
    {
    case PHOTOMETRIC_MINISWHITE:
        return "Greyscale (Min is White)";
    case PHOTOMETRIC_MINISBLACK:
        return "Greyscale (Min is Black)";
    case PHOTOMETRIC_RGB:
        return "RGB Full-Color";
    case PHOTOMETRIC_PALETTE:
        return "Palette-Color";
    case PHOTOMETRIC_MASK:
        return "Mask";
    case PHOTOMETRIC_SEPARATED:
        return "Color Separations";
    case PHOTOMETRIC_YCBCR:
        return "YCBCR Color";
    case PHOTOMETRIC_CIELAB:
        return "CIELab";
    default:
        return "Unknown type";
    }
}







TIFFOverlay::TIFFCache::TIFFCache( TIFF* pTIFF, int rowsPerStrip,
                                   UINT cacheSize )
        : d_pTIFF(pTIFF), d_rowsPerStrip(rowsPerStrip), d_cacheSize(cacheSize)
{
    Q_ASSERT( NULL != pTIFF );
    d_scanlineSize = TIFFScanlineSize( pTIFF );
    d_stripSize = TIFFStripSize( pTIFF );
    d_pStrips = new TIFFStripBuffer[cacheSize];
    qint64 calc = 0;

    // Create the strip buffers with pre-allocated buffers
    for ( int i = 0; i < d_cacheSize; i++ )
    {
        calc +=d_stripSize;
        tdata_t buf = _TIFFmalloc( d_stripSize );
        d_pStrips[i].setStripData( buf );
    }

    qDebug() << "## allocate byte " << calc <<  " ram mega -> " << weightMega(calc);
}

TIFFOverlay::TIFFCache::~TIFFCache()
{
    // Delete the strip buffer
    delete[] d_pStrips;
}






TIFFOverlay::TIFFCache::TIFFStripBuffer::TIFFStripBuffer()
        : d_pStrip(NULL), d_startRow(-1), d_endRow(-2)
{
}

TIFFOverlay::TIFFCache::TIFFStripBuffer::~TIFFStripBuffer()
{
    if ( NULL != d_pStrip )
    {
        _TIFFfree( d_pStrip );
    }
}



BYTE* TIFFOverlay::TIFFCache::getScanlineData( long row )
{
    tstrip_t strip = row / d_rowsPerStrip;
    TIFFStripBuffer& pBuffer = d_pStrips[strip % d_cacheSize];
    BYTE* pStripData = reinterpret_cast<BYTE*>( pBuffer.getStripData() );
    Q_ASSERT( NULL != pStripData );



    // See if the appropriate strip has already been loaded
    if ( !pBuffer.isRowInStrip( row ) )
    {
        TIFFReadEncodedStrip( d_pTIFF, strip, pStripData, (tsize_t)-1 );
        // Set the new start and end row of the strip
        int startRow = strip * d_rowsPerStrip;
        pBuffer.setStripLocation( startRow, startRow + d_rowsPerStrip - 1 );
    }



    // Return a pointer to the requested scanline
    return ( pStripData + d_scanlineSize * ( row % d_rowsPerStrip ) );
}


QColor TIFFOverlay::getRowColColor( long row, long col )
{
    Q_ASSERT( NULL != d_pCache );
    BYTE* pScanline = d_pCache->getScanlineData( row );
    
    if (ctype == NotSupported)
          return QColor(Qt::magenta);
    
    

    // Extract the appropriate value
    switch ( getBitsPerPixel() )
    {
    case 1:
    {
        /*  mono colors  */
        BYTE currentByte  = pScanline[ col >> 3];
        BYTE bitToExtract = 7 - ( col % 8 );
        if ( currentByte & ( 1 << bitToExtract ) )
        {
            return QColor( ctype  == BilevelMinIsBlack ? Qt::black : Qt::white );
        }
        else
        {
            return QColor( ctype  == BilevelMinIsBlack ? Qt::white : Qt::black );
        }

        break;
    }
    case 4:
    {
        BYTE index = pScanline[ col / 2 ];
        if ( 0 == ( col % 2 ) )
        {
            index = ( index & 0xF0 ) >> 4;
        }
        else
        {
            index &= 0x0F;
        }
        return QColor(Qt::magenta);
        break;
    }
    case 8:
    {
        BYTE index = pScanline[ col ];
        return QColor(Qt::magenta);
        break;
    }
    case 16:
    {
        Q_ASSERT( !isVertical() );
        int16* pData = reinterpret_cast<int16*>(pScanline);
        BYTE greyVal = pData[col];
        return QColor(Qt::magenta);
        break;
    }
    case 32:
    {
        col *= 3;
        return QColor( pScanline[col], pScanline[col + 1], pScanline[col + 2] ,255);
        break;
    }
    case 24:
        col *= 3;
        return QColor( pScanline[col], pScanline[col + 1], pScanline[col + 2] );
    }

    return QColor(Qt::magenta);
}
















































